import router from '@/router'
import { useUserStore } from '@/stores'
import type { ResData } from '@/types'
import axios, { AxiosError, type Method } from 'axios'
import { showFailToast } from 'vant'

// 在这里调用 useUserStore 获取仓库实例，能拿到吗？
// 拿不到，太早了，此时，Pinia 都还没有注册，应该放到拦截器中获取
// const userStore = useUserStore()

// 快捷键：F2（双击选中变量后重命名）
const instance = axios.create({
  baseURL: 'https://consult-api.itheima.net/',
  timeout: 10000
})

// 当有多个请求拦截器时：是按逆序执行的
// 当有多个响应拦截器时：是按正序执行的
// 推荐做法：一个拦截器，只处理一件事，不要混合到一起了，代码复杂后，难以维护

// token注入
instance.interceptors.request.use((config) => {
  const userStore = useUserStore()
  // 注入 token
  if (userStore.user?.token && config.headers) {
    config.headers.Authorization = `Bearer ${userStore.user.token}`
  }
  return config
})

// 响应数据的简化提取
instance.interceptors.response.use((res) => {
  // 先看业务有没有成功，成功了才需要简化，如果不成功，抛出异常，交给下一个拦截器处理
  if (res.data.code === 10000) {
    return res.data
  }

  // 说明：因为要统一抛出的错误类型，从而统一错误对象的数据结构
  // 创建一个 axios 的错误对象
  const axiosError = new AxiosError()
  // 将 axios 的响应对象 res 赋值给 错误对象的 response
  axiosError.response = res
  // 抛出 axios 错误对象
  return Promise.reject(axiosError)
})

// 401处理，进入失败的回调，所以成功的回调不用处理，走默认逻辑，给个undefined就行
instance.interceptors.response.use(undefined, (err: AxiosError) => {
  if (err.response?.status === 401) {
    // 删除用户信息
    const userStore = useUserStore()
    userStore.delUser()

    showFailToast('登录已过期，请重新登录！')

    // 跳转到登录页面
    router.push({
      name: 'LoginIndex',
      // router.currentRoute 当前路由对象
      // fullPath 当前完整的路由地址（不包含 协议://域名:端口）
      // 目的：为了在登录后能跳回来
      query: { returnUrl: router.currentRoute.value.fullPath }
    }) // /login?returnUrl=http://127.0.0.1:5173/xxxx

    // 后面的拦截器不用走了
    return new Promise(() => {})
  }

  // 如果不是 401 错误，那么这个拦截器就处理不了，交给下一个拦截器处理
  return Promise.reject(err)
})

// 错误消息提示处理：
//   1. 请求成功了，但是业务失败了，code !== 10000    => Axios 的错误对象
//   2. 响应失败了，404、400、500    => Axios 的错误对象
instance.interceptors.response.use(
  undefined,
  (err: AxiosError<{ message: string }>) => {
    // 问题：在这，如何区别错误情况？

    // console.log('[ err ] >', err)
    // console.log('[ err. ] >', err.response?.data?.message)

    if (err.response?.data.message) {
      showFailToast(err.response.data.message)
    } else {
      // 默认错误消息
      showFailToast('系统繁忙，请稍后重试！')
    }

    return Promise.reject(err)
  }
)

// const request = <T, D = any>(config: AxiosRequestConfig<D>) => {
//   return instance<any, ResData<T>>(config)
// }

// 泛型函数：函数表达式写法
// T: res.data 的类型, D: submitData 的类型
const request = <T, D = any>(
  url: string,
  method: Method = 'GET',
  submitData?: D
) => {
  // 快捷键：alt + 左右方向键 可以快速跳转历史光标位置
  return instance<any, ResData<T>>({
    method,
    url,
    // 这里由于 method 可以是小写或大写，所以先统一转换成大写，再进行比较，才安全
    [method.toUpperCase() === 'GET' ? 'params' : 'data']: submitData
  })
}

// 泛型函数：命名函数写法
// function request<T, D = any>(url: string, method: Method = 'GET', submitData? D) {
//   return instance<any, ResData<T>>({
//     method,
//     url,
//     // 这里由于 method 可以是小写或大写，所以先统一转换成大写，再进行比较，才安全
//     [method.toUpperCase() === 'GET' ? 'params' : 'data']: submitData
//   })
// }

export { request }

export default instance
